﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace Game_Engine
{
    public class InvertedBoxCollider : Collider
    {
        //Size is a Vector3. It represents the distance from the center of a box to any of its
        //Vertices along the three axes. It is extremely important to remember this when
        //constructing: A box with size 1 has edges with length 2.
        public Vector3 Size                                   //A size property. 
        {                                                   
            get;
            set;
        }

        public InvertedBoxCollider()
        {
            Name = "InvertedBoxCollider";
            Size = Vector3.One;
        }

        private Vector3[] normals =                         //Six Face Normals
        {
            Vector3.Down, Vector3.Up, 
            Vector3.Right, Vector3.Left,
            Vector3.Backward, Vector3.Forward
        };                                                                          //Normals and vertices are given in object space.
        private Vector3[] vertices =                    //Box Vertices
        {
            new Vector3(-1, -1, 1),
            new Vector3(1, -1, 1),
            new Vector3(1, -1, -1),
            new Vector3(-1, -1, -1),

            new Vector3(-1, 1, 1),
            new Vector3(1, 1, 1),
            new Vector3(1, 1, -1),
            new Vector3(-1, 1, -1),
        };
        private int[] indices =                     //Triangle Vertex Indices
        {
            0, 2, 1,     0, 3, 2,                     //Down              //A few things at play here:
            7, 5, 6,     7, 4, 5,                     //Up                    //Edge Winding--We must vary the ordering of the vertices so that a face's normal corresponds to
            1, 6, 5,     1, 2, 6,                     //Right
            4, 3, 0,     4, 7, 3,                     //Left                 //a counter-clockwise list
            4, 1, 5,     4, 0, 1,                     //Back                            //Inverted Box Collider            
            3, 6, 2,     3, 7, 6,                     //Front                 //Additionally we must arrange the faces such that the normals point inward. This is a class for an  
            
        };
        public override bool Collides(Collider collider, out Vector3 normalOut)
        {
            if(collider is SphereCollider)
            {                                                   //Determine our collision partner is a sphere.
                SphereCollider sc = collider as SphereCollider;     //Change our local variable's type
                normalOut = Vector3.Zero;
                bool isColliding = false;
                for(int i = 0; i < 6; i++)                  //Loop through face normal for collision testing -- a total of six faces
                {
                    for(int j = 0; j < 2; j++)              //Each face is composed of two triangles which are separately considered
                    {
                        Vector3 x = vertices[indices[i * 6 + j * 3]]*Size.X;      //Here we extract the vertices corresponding to the point of collision on the face of the cube.
                        Vector3 y = vertices[indices[i * 6 + j * 3 + 1]]*Size.Y;  //For each coordinate we determine the face we are looping through (i * 6) and the triangle we are
                        Vector3 z = vertices[indices[i * 6 + j * 3 + 2]]*Size.Z;  //considering (j * 3). The sum of these values yields a vertex index from the array indices[], which
                        Vector3 normal = normals[i];                            //in turn yields a vertex from the array vertices[]. The face index i is used to access the normal from normals[].

                        float d = Math.Abs(Vector3.Dot(sc.Transform.Position - x, normal)); //The dot product of the (vector spanning the two colliders) and (the normal of the cube face) is equivalent
                                                                                              //to the magnitude of the smaller vector projected onto the larger. Regardless, if this value is less than  
                        if(d < sc.Radius)                                                   //the radius of the sphere collider the geometry overlaps. Why do we use x? Would y or z suffice?
                        {
                            Vector3 pointOnPlane = sc.Transform.Position - normal * d;                  //Since d is an expression of the magnitude of (vector spanning the two colliders) projected onto
                            float area1 = Vector3.Dot(Vector3.Cross(y - x, pointOnPlane - x), normal);  //the normal, it is a convenient expression of the actual distance between the two objects.
                            float area2 = Vector3.Dot(Vector3.Cross(z - y, pointOnPlane - y), normal);  //Vector3 pointOnPlane gets a point on the triangle of interest by scaling the normal by d and 
                            float area3 = Vector3.Dot(Vector3.Cross(x - z, pointOnPlane - z), normal);  //subtracting the result from the center of the offending sphere.
                                                                                                        //What we are computing are the areas of three triangles dividing the face in three, the sizes of 
                            if (!(area1 < 0 || area2 < 0 || area3 < 0))                                 //each based on the PoP relative to the three vertices. If none of the areas are less than 0 we have a collision
                            {                                                                           //If any of them are negative it means the new triangle actually occurs off of the face
                                normalOut += normal;
                                isColliding = true;
                                j = 1;                      //If we find a triangle on a face, skip the second one
                                if (i % 2 == 0) i += 1;     //Skip opposite side of box collider

                                Console.WriteLine("X:" + x);
                                Console.WriteLine("Y:" + y);
                                Console.WriteLine("Z:" + z);
                                Console.WriteLine("Normal:" + normal);
                                Console.WriteLine("D:" + d);  
                                Console.WriteLine("PoP:" + pointOnPlane);
                                Console.WriteLine("area1:" + area1);
                                Console.WriteLine("area2:" + area2);
                                Console.WriteLine("area3:" + area3);
                            }
                        }
                    }
                }
                if (isColliding)
                    normalOut.Normalize();
                return isColliding;
            }
            return base.Collides(collider, out normalOut);
        }

        public override float? Intersects(Ray ray)
        {
            toObject(ray);

            BoundingBox box = new BoundingBox(-Vector3.One * Size, Vector3.One * Size); //Min/Max bounding box inclusion sizes
            return box.Intersects(ray);
        }
    }
}
